#第十一章 接口：从协议到抽象基类
'''
鸭子模型 ： 
即忽略对象的真正类型，转而关注对象有没有实现所需的方法、签名和语义
例如某个类实现了__getitem__方法那么它就支持下标访问了 不需要继承序列抽象基类

'''

import collections

Card = collections.namedtuple("Card",'ranks suits')

class FrenchDeck(collections.MutableSequence): #实现接口
    ranks = [str(n) for n in range(2,11)]+list("JQKA")
    suits = 'spades diamonds clubs hearts'.split()
    def __init__(self):
        self._cards = [Card(rank,suit) for rank in self.ranks for suit in self.suits]
        #print(self._cards)

    def __len__(self):
        return len(self._cards)
    def __getitem__(self,pos):
        return self._cards[pos]
    def __setitem__(self,value,index):
        self._cards[index] = value
    def __delitem__(self,index):
        del self._cards[index]
    def insert(self,index,value):
        self._cards.insert(index,value)
    
    
fd = FrenchDeck()
print(fd,id(fd),hash(fd))

#定义一个抽象基类

import abc
import random


'''
python2定义抽象类
class Tombola(object):
    __metaclass__ = abc.AbCMeta

'''
class Tombola(abc.ABC): #python3.4开始可以这样定义抽象类 python3的需要 Tombola(metaclass=abc.ABCMeta) 
    @abc.abstractmethod
    def load(self,iterable): #定义一个抽象方法
        pass

    @abc.abstractmethod
    def pick(self):
        pass

    def inspect(self):
        items = []
        while True:
            try:
                items.append(self.pick())
            except LookupError:
                break
            self.load(items)

        return tuple(sorted(items))

    def loaded(self):
        return bool(self.inspect())
'''
class Fake(Tombola): #继承抽象类 必须实现抽象类的所有抽象方法否则实例化的时候会报错
    def pick(self):
        return 12

f = Fake()

r = random.SystemRandom() #返回一个根据系统随机种子的随机实例
print(r,r.random(),random.random())

'''

class BingoCage(Tombola):
    def __init__(self,items):
        self._randomizer = random.SystemRandom() #返回一个根据系统随机种子的随机实例 os.urandom(...)
        self._items = []
        self.load(items)
    def load(self,items):
        self._items.extend(items)
        self._randomizer.shuffle(self._items)

    def pick(self):
        try:
            return self._items.pop()
        except Exception:
            raise LookupError("pick from empty bingocage")

    def __call__(self):
        self.pick()
            
#抽象基类的register装饰器的使用

@Tombola.register #使用了该装饰器后 该类是Tombola的一个虚拟子类 但是你不需要实现接口中的抽象方法也可以实例化
class TombolaList(list):
    def say(self):
        print("Hello ")

tl = TombolaList() #可以正常实例化
tl.say() 

print(issubclass(TombolaList,Tombola)) #可以检测到是tombola的子类
print(TombolaList.__mro__,type(TombolaList.__mro__)) #按顺序列出类及其超类 可以得到一个类的继承顺序 

'''
复习:实例化 list tuple set 的方法

ary = [1,2,3]
tp = (1,2,3)
st = {1,2,3}
print(ary,tp,st,type(ary),type(tp),type(st))

'''
print(Tombola.__subclasses__()) #得到子类列表


